package com.hmdp.service.impl;

import cn.hutool.core.util.StrUtil;
import com.hmdp.constant.RedisConstants;
import com.hmdp.constant.SystemConstants;
import com.hmdp.entity.Shop;
import com.hmdp.mapper.ShopMapper;
import com.hmdp.service.IShopService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.CacheClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.domain.geo.GeoReference;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private CacheClient cacheClient;

    /**
     * 根据id查询商铺信息
     * @param id
     * @return
     */
    @Override
    public Shop queryById(Long id){
        //解决缓存穿透
        Shop shop = cacheClient.queryWithPassThrough(RedisConstants.CACHE_SHOP_KEY,
                id,
                Shop.class,
                RedisConstants.CACHE_SHOP_TTL,
                TimeUnit.MINUTES,
                this::getById);

       //互斥锁解决缓存击穿
  /*      Shop shop = null;
        try {
            shop = cacheClient.queryWithLock(RedisConstants.CACHE_SHOP_KEY,
                    id,
                    Shop.class,
                    RedisConstants.CACHE_SHOP_TTL,
                    TimeUnit.MINUTES, this::getById);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("互斥锁异常");
        }*/

  /*      //逻辑过期解决缓存击穿
        Shop shop = cacheClient.queryWithLogicalExpire(RedisConstants.CACHE_SHOP_KEY,
                id,
                Shop.class,
                RedisConstants.CACHE_SHOP_TTL,
                TimeUnit.MINUTES, this::getById);*/

        if(shop == null){
            throw new RuntimeException("店铺信息不存在");
        }
        return shop;
    }

    /**
     * 根据商铺类型分页查询商铺信息
     *
     * @param typeId
     * @param current
     * @param x
     * @param y
     * @return
     */
    @Override
    public List<Shop> queryShopByType(Integer typeId, Integer current, Double x, Double y) {
     //当前页面起始
        Integer begin = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;
        Integer end = current * SystemConstants.DEFAULT_PAGE_SIZE;

        //查找用户位置为圆心，半径为5km的圆形区域内的店铺id并分页
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(RedisConstants.SHOP_GEO_KEY + typeId,
                GeoReference.fromCoordinate(x, y),
                new Distance(5000),
                RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().limit(end));

        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> resultList = results.getContent();
        if(resultList == null || resultList.size() == 0){
            return Collections.emptyList();
        }
        //没有下一页，结束
        if(resultList.size()<begin){
            return Collections.emptyList();
        }

        List<Long> ids = new ArrayList<>();
        Map<String,Double> distanceMap = new HashMap<>();
        resultList.stream().skip(begin).forEach(geoResult -> {
            RedisGeoCommands.GeoLocation<String> location = geoResult.getContent();
            Distance distance = geoResult.getDistance();
            String id = location.getName();
            distanceMap.put(id,distance.getValue());
            ids.add(Long.valueOf(id));
        });

        String idstr = StrUtil.join(",", ids);
        //根据id查找店铺
        List<Shop> shops = query().in("id", ids).last("order by field(id," + idstr + ")").list();
        for(Shop shop : shops){
            shop.setDistance(distanceMap.get(shop.getId().toString()));
        }
        return shops;
    }
}
